%%%-------------------------------------------------------------------
%%% File    : iter_cache.erl
%%% Author  :  <vjache>
%%% Description : 
%%%
%%% Created :  4 Aug 2009 by  <vjache>
%%%-------------------------------------------------------------------
-module(iter_cache).

%% API
-export([
		 new/1,
		 new/2,
		 open/1,
		 next/1,
		 close/1,
		 clear/0
		 ]).

%%--------------------------------------------------------------------
%% Include files
%%--------------------------------------------------------------------
-include_lib("eunit/include/eunit.hrl").

-include("../include/iter.hrl").

%%====================================================================
%% API Functions
%%====================================================================
%%--------------------------------------------------------------------
%% Function: 
%% Description:
%%--------------------------------------------------------------------
new(BaseIter) ->
	new(BaseIter,false).
new(BaseIter,KeepOrder) when 
  is_record(BaseIter,iter),is_boolean(KeepOrder) ->
	Id=make_ref(),
	iter:from_module(?MODULE, {BaseIter,Id,KeepOrder}).

open({BaseIter,Id,KeepOrder}) ->
	case dict:find(Id, get_cache()) of
		error -> {iter:open(BaseIter),Id,[],KeepOrder};
		{ok, Buff} ->
			{Buff}
	end.

next({[]=Buff}) ->
	{{Buff},?EOF};
next({[Item|Tail]=_Buff}) ->
	{{Tail},Item};
next({BaseIterState,Id,Buff,KeepOrder}) ->
	{BaseIterState1,Item}=iter:next(BaseIterState),
	case Item of
		?EOF ->
			iter:close(BaseIterState1),
			{{?EOF,Id,Buff,KeepOrder},Item};
		_ ->
			{{BaseIterState1,Id,[Item|Buff],KeepOrder},Item}
	end.
close({_Buff}) ->
	ok;
close({?EOF,Id,Buff,KeepOrder}) ->
	update_cache(
	  dict:store(Id, 
				 case KeepOrder of
					 true->
						 lists:reverse(Buff);
					 false->
						 Buff
				 end, get_cache())),
	ok;
close({BaseIterState,_Id,_Buff,_KeepOrder}) ->
	iter:close(BaseIterState).

clear() ->
	erase(?MODULE).
%%====================================================================
%% Internal functions
%%====================================================================
get_cache() ->
	case get(?MODULE) of
		undefined -> put(?MODULE,D=dict:new()),D;
		D -> D
	end.

update_cache(Dict) ->
	put(?MODULE,Dict).

%%====================================================================
%% eunit test functions
%%====================================================================

basic_test() ->
	clear(),
	TestData=lists:seq(1, 10000),
	IterCache=new(iter:from_list(TestData)),
	iter:print(IterCache),
	?assertNot(get(?MODULE)==undefined),
	[{_,Data}]=dict:to_list(get_cache()),
	?assertEqual(TestData,lists:reverse(Data)),
	clear(),
	?assert(get(?MODULE)==undefined).

twice_test() ->
	clear(),
	TestData=lists:seq(1, 10000),
	IterCache=new(iter:from_list(TestData)),
	iter:print(IterCache),
	?assertNot(get(?MODULE)==undefined),
	iter:print(IterCache),
	iter:print(IterCache),
	?assertNot(get(?MODULE)==undefined),
	[{_,Data}]=dict:to_list(get_cache()),
	?assertEqual(TestData,lists:reverse(Data)),
	clear(),
	?assert(get(?MODULE)==undefined).
basic_ko_test() ->
	clear(),
	TestData=lists:seq(1, 10000),
	IterCache=new(iter:from_list(TestData),true),
	iter:print(IterCache),
	?assertNot(get(?MODULE)==undefined),
	[{_,Data}]=dict:to_list(get_cache()),
	?assertEqual(TestData,Data),
	clear(),
	?assert(get(?MODULE)==undefined).

twice_ko_test() ->
	clear(),
	TestData=lists:seq(1, 10000),
	IterCache=new(iter:from_list(TestData),true),
	iter:print(IterCache),
	?assertNot(get(?MODULE)==undefined),
	iter:print(IterCache),
	iter:print(IterCache),
	?assertNot(get(?MODULE)==undefined),
	[{_,Data}]=dict:to_list(get_cache()),
	?assertEqual(TestData,Data),
	clear(),
	?assert(get(?MODULE)==undefined).

interference_test() ->
	clear(),
	?assert(get(?MODULE)==undefined),
	TestData=lists:seq(1, 10000),
	IterCache=new(iter:from_list(TestData),true),
	TestData1=lists:seq(1, 100),
	IterCache1=new(iter:from_list(TestData1),true),
	iter:print(IterCache),
	iter:print(IterCache1),
	?assertNot(get(?MODULE)==undefined),
	?assertMatch([_,_],dict:to_list(get_cache())),
	?assertEqual(TestData,iter:to_list(IterCache)),
	?assertEqual(TestData1,iter:to_list(IterCache1)),
	clear(),
	?assert(get(?MODULE)==undefined).